package com.cs5248.mp4Parser;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;

/**
 * Shortens/Crops a track
 */
public class MovieSplitter {
	
	public static int SPLIT_DURATION_SECONDS = 3;

	public static void main(String[] args) throws IOException {
		MovieSplitter splitter = new MovieSplitter();
		String fileInput = "C:\\Users\\ziqiang\\Desktop\\video\\aaa.mp4";
		splitter.splitVideo(fileInput, "C:\\Users\\ziqiang\\Desktop\\video\\output", SPLIT_DURATION_SECONDS, "testVideo");
	}
	
	@SuppressWarnings("resource")
	public void splitVideo(String inputFile, String destionation, int duration, String outputname)
			throws IOException {
		Movie movie = MovieCreator.build(inputFile);

		IsoFile isoFile = new IsoFile(inputFile);
		System.out.println(isoFile.getSize());
		double lengthInSeconds = (double) isoFile.getMovieBox().getMovieHeaderBox().getDuration()
				/ isoFile.getMovieBox().getMovieHeaderBox().getTimescale();

		int outCnt = (int) Math.ceil(lengthInSeconds / duration);

		System.err.println("Movie Length =" + lengthInSeconds + " number of output files = " + outCnt);

		List<Track> tracks = movie.getTracks();
		movie.setTracks(new LinkedList<Track>());

		boolean timeCorrected = false;

		for (Track track : tracks) {
			if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
				System.out.println(track.getSyncSamples().length + "   " + tracks.size());
				if (timeCorrected) {
					throw new RuntimeException(
							"The startTime has already been corrected by another track with SyncSample. Not Supported.");
				}

				for (int i = 0; i < outCnt; i++) {

					double startTime = i * duration;
					double endTime = (i + 1) * duration;

					startTime = correctTimeToSyncSample(track, startTime, false);
					endTime = correctTimeToSyncSample(track, endTime, endTime > lengthInSeconds);
					System.out.println(startTime + " ------ " + endTime);

					createSegmentedVideo(tracks, startTime, endTime, destionation, outputname, i);

				}
				timeCorrected = true;
			}
		}
	}

	private void createSegmentedVideo(List<Track> tracks, double starTime, double endTime, String destionation,
			String outputname, int index) throws IOException {
		Movie outmovie = new Movie();
		for (Track track : tracks) {
			long currentSample = 0;
			double currentTime = 0;
			double lastTime = 0;
			long startSample1 = 0;
			long endSample1 = 0;

			for (int i = 0; i < track.getSampleDurations().length; i++) {
				long delta = track.getSampleDurations()[i];

				if (currentTime > lastTime && currentTime <= starTime) {
					startSample1 = currentSample;
				}
				if (currentTime > lastTime && currentTime <= endTime) {
					endSample1 = currentSample;
				}
				lastTime = currentTime;
				currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
				currentSample++;
			}
			outmovie.addTrack(new AppendTrack(new CroppedTrack(track, startSample1,
					Math.abs(starTime - endTime) < 0.01 ? currentSample : endSample1)));
		}

		long start1 = System.currentTimeMillis();
		Container out = new DefaultMp4Builder().build(outmovie);
		long start2 = System.currentTimeMillis();
		// FileOutputStream fos = new FileOutputStream(String.format("output-%f-%f.mp4", starTime, endTime));
		FileOutputStream fos = new FileOutputStream(String.format(destionation + "\\" + outputname + "%04d.mp4",
				index + 1));
		FileChannel fc = fos.getChannel();
		out.writeContainer(fc);

		fc.close();
		fos.close();
		long start3 = System.currentTimeMillis();
		System.err.println("Building IsoFile took : " + (start2 - start1) + "ms");
		System.err.println("Writing IsoFile took  : " + (start3 - start2) + "ms");
		System.err.println("Writing IsoFile speed : "
				+ (new File(String.format("output-%f-%f.mp4", starTime, endTime)).length() / (start3 - start2) / 1000)
				+ "MB/s");

	}

	private double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
		double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
		long currentSample = 0;
		double currentTime = 0;
		for (int i = 0; i < track.getSampleDurations().length; i++) {
			long delta = track.getSampleDurations()[i];

			if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
				timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
			}
			currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
			currentSample++;
		}
		double previous = 0;
		for (double timeOfSyncSample : timeOfSyncSamples) {
			if (timeOfSyncSample > cutHere) {
				if (next) {
					return timeOfSyncSample;
				} else {
					return previous;
				}
			}
			previous = timeOfSyncSample;
		}

		return timeOfSyncSamples[timeOfSyncSamples.length - 1];
	}

}
